package main;

//import java.util.LinkedHashSet;
//import java.util.Set;
import java.util.ArrayList;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;

import uniqueEquipment.HornOfGondor;

public class Battle {
	
	public UI myUI = null;
	public Game game;
	public static Battle currBattle = null;
	
	public static Random rng = new Random();
	
	public ArrayList<Unit> goodUnits;
	public ArrayList<Unit> badUnits;
	
	public ArrayList<Unit> losers;
	public ArrayList<Unit> winners;
	
	public int majority = -1;
	public int victor = -1;
	
	public int chargerIndex = -1;
	public boolean cavalryCharge = false;
	
	public boolean hornOfGondorBlown = false;
	
	public Battle(Game game) {
		goodUnits = new ArrayList<Unit>();
		badUnits = new ArrayList<Unit>();
		this.game = game;
		this.myUI = game.myUI;
	}
	
	public void addUnit(Unit u) {
		if (u.armyIndex == Game.GOODINDEX)
			this.goodUnits.add(u);
		else
			this.badUnits.add(u);
		if (goodUnits.size() > 1)
			majority = 0;
		else if (badUnits.size() > 1)
			majority = 1;
	}
	
	public ArrayList<Unit> getUnitsByIndex(int armyIndex) {
		if (armyIndex == Game.GOODINDEX) {
			return goodUnits;
		}
		else if (armyIndex == Game.BADINDEX) {
			return badUnits;
		}
		return null;
	}
	
	public ArrayList<Unit> getAllUnits() {
		ArrayList<Unit> allUnits = new ArrayList<Unit>();
		for (Unit u : goodUnits)
			allUnits.add(u);
		for (Unit u : badUnits)
			allUnits.add(u);
		return allUnits;
	}
	
	public void removeUnit(Unit u) {
		if (u.armyIndex == Game.GOODINDEX)
			this.goodUnits.remove(u);
		else
			this.badUnits.remove(u);
		if (this.goodUnits.size() <= 0) {
			game.battles.remove(this);
			for (Unit t : badUnits) {
				t.battle = null;
			}
		}
		if (this.badUnits.size() <= 0) {
			game.battles.remove(this);
			for (Unit t : goodUnits) {
				t.battle = null;
			}
		}
	}
	
	
	public void rollFightDice(Button nextButton) {
		// Check for cavalry charge
		for (Unit u : this.getUnitsByIndex(chargerIndex)) {
			if (u.isCavalry())
				this.cavalryCharge = true;
		}
		for (Unit u : this.getUnitsByIndex((chargerIndex+1)%2)) {
			if (u.isCavalry())
				this.cavalryCharge = false;
		}
		
		if (!hornOfGondorBlown && badUnits.size() > goodUnits.size()) {
			// Check if anyone has one
			for (Unit u : goodUnits) {
				if (u.hasEquipment(new HornOfGondor())){
					rollFightDiceWithHorn(nextButton);
					return;
				}
			}
		}
		
		ArrayList<DieRoll> goodRolls = new ArrayList<DieRoll>();
		ArrayList<DieRoll> badRolls = new ArrayList<DieRoll>();
		int goodFV = 0;
		int badFV = 0;
		boolean mightPossible = false;
		// Roll the dice
		int dieCount = 1;
		for (Unit u : goodUnits) {
			u.hasFought = true;
			if (u.getFightValue() > goodFV)
				goodFV = u.getFightValue();
			int fightDice = u.getBattleDice();
			for (int i = 0; i < fightDice; i++) {
				DieRoll goodRoll = new DieRoll(u, myUI.dim.width-200, myUI.dim.height/2-90-30*(dieCount++), u.getBattleBonus(), u.toChoiceString(), myUI);
				goodRolls.add(goodRoll);
				if (goodRoll.isModifiable())
					mightPossible = true;
			}
		}
		dieCount = 1;
		for (Unit u : badUnits) {
			u.hasFought = true;
			if (u.getFightValue() > badFV)
				badFV = u.getFightValue();
			int fightDice = u.getBattleDice();
			for (int i = 0; i < fightDice; i++) {
				DieRoll badRoll = new DieRoll(u, myUI.dim.width-200, myUI.dim.height/2-90+30*(dieCount++), u.getBattleBonus(), u.toChoiceString(), myUI);
				badRolls.add(badRoll);
				if (badRoll.isModifiable())
					mightPossible = true;
			}
		}
		
		// None of our rollers can use might
		if (mightPossible == false){
			UI.messageLog.add("Good rolls, fightValue: " + goodRolls.toString() + ", " + goodFV);
			UI.messageLog.add("Bad rolls, fightValue: " + badRolls.toString() + ", " + badFV);
			
			int goodMax = 0;
			for (DieRoll dr : goodRolls) {
				if (goodMax < dr.getRoll())
					goodMax = dr.getRoll();
			}
			int badMax = 0;
			for (DieRoll dr : badRolls) {
				if (badMax < dr.getRoll())
					badMax = dr.getRoll();
			}
			
			if (goodMax > badMax || (goodMax == badMax && goodFV > badFV) ||
					(goodMax == badMax && goodFV == badFV && rng.nextInt(2) == 0)) {
				UI.messageLog.add("Good wins");
				this.victor = Game.GOODINDEX;
				this.winners = this.goodUnits;
				this.losers = this.badUnits;
			}
			else {
				UI.messageLog.add("Bad wins");
				this.victor = Game.BADINDEX;
				this.winners = this.badUnits;
				this.losers = this.goodUnits;
			}
			
			for (Unit u : this.losers){
				u.moveLeft = 1;
				// If lost to charging cavalry, get knocked down
				if (this.chargerIndex == this.victor && this.cavalryCharge && u.getStrength() < 6)
					u.knockDown();
				if (u instanceof Hero && ((Hero)u).hasFury)
					((Hero)u).hasFury = false;
				u.uncontactTerrain();
				u.uncontactUnits();
				for (Unit ally : u.army.units) {
					if (Utils.inchDist(u, ally) < 2 && ally.battle == null) {
						ally.moveLeft = 1;
					}
				}
			}
			for (DieRoll dr : goodRolls)
				dr.cleanup();
			for (DieRoll dr : badRolls)
				dr.cleanup();
			nextButton.setEnabled(true);
			nextButton.setVisible(true);
			myUI.reenableComponents();
			myUI.repaint();
		}
		// Otherwise, someone can use might
		else {
			final int goodFVfinal = goodFV;
			final int badFVfinal = badFV;
			ActionListener mightDone = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					UI.messageLog.add("Good rolls, fightValue: " + goodRolls.toString() + ", " + goodFVfinal);
					UI.messageLog.add("Bad rolls, fightValue: " + badRolls.toString() + ", " + badFVfinal);
					
					int goodMax = 0;
					for (DieRoll dr : goodRolls) {
						if (goodMax < dr.getRoll())
							goodMax = dr.getRoll();
					}
					int badMax = 0;
					for (DieRoll dr : badRolls) {
						if (badMax < dr.getRoll())
							badMax = dr.getRoll();
					}
					
					if (goodMax > badMax || (goodMax == badMax && goodFVfinal > badFVfinal) ||
							(goodMax == badMax && goodFVfinal == badFVfinal && rng.nextInt(2) == 0)) {
						UI.messageLog.add("Good wins");
						victor = Game.GOODINDEX;
						winners = goodUnits;
						losers = badUnits;
					}
					else {
						UI.messageLog.add("Bad wins");
						victor = Game.BADINDEX;
						winners = badUnits;
						losers = goodUnits;
					}
					
					for (Unit u : losers){
						u.moveLeft = 1;
						// If lost to charging cavalry, get knocked down
						if (chargerIndex == victor && cavalryCharge && u.getStrength() < 6)
							u.knockDown();
						if (u instanceof Hero && ((Hero)u).hasFury)
							((Hero)u).hasFury = false;
						u.uncontactTerrain();
						u.uncontactUnits();
						for (Unit ally : u.army.units) {
							if (Utils.inchDist(u, ally) < 2 && ally.battle == null) {
								ally.moveLeft = 1;
							}
						}
					}
					
					for (DieRoll dr : goodRolls)
						dr.cleanup();
					for (DieRoll dr : badRolls)
						dr.cleanup();
					myUI.disableMight();
					nextButton.setEnabled(true);
					nextButton.setVisible(true);
					myUI.reenableComponents();
					myUI.repaint();
				}
			};
			myUI.enableMight(mightDone);
		}
		
		myUI.revalidate();
		myUI.repaint();
	}
	
	
	private void rollFightDiceWithHorn(Button nextButton) {
		// A good unit has blown the Horn of Gondor
		// Find the bad unit with highest courage to take a test
		Unit courageTesterTemp = badUnits.get(0);
		for (Unit u : badUnits) {
			if (u.getCourage() > courageTesterTemp.getCourage()) {
				courageTesterTemp = u;
			}
		}
		
		final Unit courageTester = courageTesterTemp;
		
		Button doneTesting = new Button();
		doneTesting.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent arg0) {
				if(courageTester.passedChargeTest) {
					// Passed the courage test, rerun the fight like normal
					hornOfGondorBlown = true;
					rollFightDice(nextButton);
				}
				else {
					// Failed the courage test, bad side loses the fight
					UI.messageLog.add("Failed the Horn of Gondor courage test, Good side auto-wins the fight!");
					victor = Game.GOODINDEX;
					winners = goodUnits;
					losers = badUnits;
					
					for (Unit u : losers){
						u.moveLeft = 1;
						// If lost to charging cavalry, get knocked down
						if (chargerIndex == victor && cavalryCharge && u.getStrength() < 6)
							u.knockDown();
						if (u instanceof Hero && ((Hero)u).hasFury)
							((Hero)u).hasFury = false;
						u.uncontactTerrain();
						u.uncontactUnits();
						for (Unit ally : u.army.units) {
							if (Utils.inchDist(u, ally) < 2 && ally.battle == null) {
								ally.moveLeft = 1;
							}
						}
					}

					nextButton.setEnabled(true);
					nextButton.setVisible(true);
					myUI.reenableComponents();
					myUI.repaint();
				}
			}
			public void mouseEntered(MouseEvent arg0) {}
			public void mouseExited(MouseEvent arg0) {}
			public void mousePressed(MouseEvent arg0) {}
			public void mouseReleased(MouseEvent arg0) {}
		});
		UI.messageLog.add("Blowing the Horn of Gondor, " + courageTester.toChoiceString() + " taking courage test");
		courageTester.takeCourageTest(doneTesting, "Charge");
	}

	public void refreshWinnersLosers(Choice attackers, Choice targets) {
		attackers.removeAll();
		targets.removeAll();
		for (Unit u : this.winners) {
			for (int i = 0; i < u.getWoundDice()+Math.min(1, u.supporters.size()); i++) {
				attackers.add(u.toChoiceString());
			}
		}
		for (Unit u : this.losers) {
			for (int i = 0; i < u.getWounds(); i++) {
				if (u.hasRetreated)
					targets.add(u.toChoiceString());
				else
					targets.add(u.toChoiceString() + "-(T)");
			}
		}
	}
	
	public void cleanUp() {
		// Deal with dead people
		for (Unit u : goodUnits) {
			if (u.getWounds() <= 0) {
				goodUnits.remove(u);
				game.armies[Game.GOODINDEX].units.remove(u);
			}
		}
		for (Unit u : badUnits) {
			if (u.getWounds() <= 0) {
				badUnits.remove(u);
				game.armies[Game.BADINDEX].units.remove(u);
			}
		}
		
		// no one's in a fight anymore
		game.battles.remove(this);
		for (Unit u : goodUnits)
			u.battle = null;
		for (Unit u : badUnits)
			u.battle = null;
		
		// reset everyone's moves to 0
		for (Army a : game.armies) {
			for (Unit u : a.units){
				u.moveLeft = 0;
			}
		}

		
		Battle.currBattle = null;
		myUI.revalidate();
		myUI.repaint();
	}
	
	public Unit getSelectedUnit(Choice c) {
		int id = Integer.parseInt(c.getSelectedItem().split("-")[1]);
		for (Unit u : goodUnits) {
			if (u.id == id)
				return u;
		}
		for (Unit u : badUnits) {
			if (u.id == id)
				return u;
		}
		return null;
	}
	
	public String toString() {
		String s = "";
		s += "Good- ";
		for (Unit u : goodUnits){
			s += u.name + " " + u.id + " ";
		}
		s += "Bad- ";
		for (Unit u : badUnits){
			s += u.name + " " + u.id + " ";
		}
		return s;
	}
	
}